home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue72 / alfresco / AABWT.pas next >
Encoding:
Pascal/Delphi Source File  |  2001-07-05  |  14.5 KB  |  533 lines

  1. {*********************************************************}
  2. {* AABWT                                                 *}
  3. {* Copyright (c) Julian M Bucknall 2001                  *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Algorithms Alfresco: Burrows-Wheeler compression      *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AABWT;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils,
  19.   Classes;
  20.  
  21. procedure AABWTCompress(aInStream, aOutStream : TStream);
  22.  
  23. procedure AABWTUncompress(aInStream, aOutStream : TStream);
  24.  
  25. implementation
  26.  
  27. uses
  28.   AAHuffmn;
  29.  
  30. type
  31.   PIntArray = ^TIntArray;
  32.   TIntArray = array [0..pred(MaxInt div sizeof(integer))] of integer;
  33.  
  34. {===Quicksort for BWT================================================}
  35. const
  36.   QSCutOff = 15;
  37. {--------}
  38. function CompareBlocks(aData1, aData2 : pointer;
  39.                        aSize : integer) : integer;
  40. var
  41.   Data1 : PChar;
  42.   Data2 : PChar;
  43.   i     : integer;
  44. begin
  45.   Data1 := aData1;
  46.   Data2 := aData2;
  47.   i := aSize;
  48.   while (i > 0) and (Data1^ = Data2^) do begin
  49.     dec(i);
  50.     inc(Data1);
  51.     inc(Data2);
  52.   end;
  53.   if (i = 0) then
  54.     Result := 0
  55.   else if (Data1^ < Data2^) then
  56.     Result := -1
  57.   else
  58.     Result := +1;
  59. end;
  60. {--------}
  61. procedure QSInsertionSort(aList    : PPointerList;
  62.                           aFirst   : integer;
  63.                           aLast    : integer;
  64.                           aSize    : integer);
  65. var
  66.   i, j       : integer;
  67.   IndexOfMin : integer;
  68.   Temp       : pointer;
  69. begin
  70.   {find the smallest element in the first QSCutOff items and put it in
  71.    the first position}
  72.   IndexOfMin := aFirst;
  73.   j := QSCutOff;
  74.   if (j > aLast) then
  75.     j := aLast;
  76.   for i := succ(aFirst) to j do
  77.     if (CompareBlocks(aList^[i], aList^[IndexOfMin], aSize) < 0) then
  78.       IndexOfMin := i;
  79.   if (aFirst <> IndexOfMin) then begin
  80.     Temp := aList^[aFirst];
  81.     aList^[aFirst] := aList^[IndexOfMin];
  82.     aList^[IndexOfMin] := Temp;
  83.   end;
  84.   {now sort via fast insertion method}
  85.   for i := aFirst+2 to aLast do begin
  86.     Temp := aList^[i];
  87.     j := i;
  88.     while (CompareBlocks(Temp, aList^[j-1], aSize) < 0) do begin
  89.       aList^[j] := aList^[j-1];
  90.       dec(j);
  91.     end;
  92.     aList^[j] := Temp;
  93.   end;
  94. end;
  95. {--------}
  96. procedure QS(aList    : PPointerList;
  97.              aFirst   : integer;
  98.              aLast    : integer;
  99.              aSize    : integer);
  100. var
  101.   L, R  : integer;
  102.   Pivot : pointer;
  103.   Temp  : pointer;
  104. begin
  105.   while ((aLast - aFirst) > QSCutOff) do begin
  106.     {sort the first, middle and last items, then set the pivot to the
  107.      middle one - the median-of-3 method}
  108.     R := (aFirst + aLast) div 2;
  109.     if (CompareBlocks(aList^[aFirst], aList^[R], aSize) > 0) then begin
  110.       Temp := aList^[aFirst];
  111.       aList^[aFirst] := aList^[R];
  112.       aList^[R] := Temp;
  113.     end;
  114.     if (CompareBlocks(aList^[aFirst], aList^[aLast], aSize) > 0) then begin
  115.       Temp := aList^[aFirst];
  116.       aList^[aFirst] := aList^[aLast];
  117.       aList^[aLast] := Temp;
  118.     end;
  119.     if (CompareBlocks(aList^[R], aList^[aLast], aSize) > 0) then begin
  120.       Temp := aList^[R];
  121.       aList^[R] := aList^[aLast];
  122.       aList^[aLast] := Temp;
  123.     end;
  124.     Pivot := aList^[R];
  125.     {set indexes and partition}
  126.     L := aFirst;
  127.     R := aLast;
  128.     while true do begin
  129.       repeat dec(R); until (CompareBlocks(aList^[R], Pivot, aSize) <= 0);
  130.       repeat inc(L); until (CompareBlocks(aList^[L], Pivot, aSize) >= 0);
  131.       if (L >= R) then Break;
  132.       Temp := aList^[L];
  133.       aList^[L] := aList^[R];
  134.       aList^[R] := Temp;
  135.     end;
  136.     {quicksort the first subfile}
  137.     QS(aList, aFirst, R, aSize);
  138.     {quicksort the second subfile - recursion removal}
  139.     aFirst := succ(R);
  140.   end;
  141. end;
  142. {--------}
  143. procedure Quicksort(aList    : PPointerList;
  144.                     aFirst   : integer;
  145.                     aLast    : integer;
  146.                     aSize    : integer);
  147. begin
  148.   QS(aList, aFirst, aLast, aSize);
  149.   QSInsertionSort(aList, aFirst, aLast, aSize);
  150. end;
  151. {====================================================================}
  152.  
  153.  
  154. {===Distribution sort for BWT========================================}
  155. procedure DistSort(aInBlock, aOutBlock : PChar; aSize : integer;
  156.                    aStartPos : PIntArray);
  157. var
  158.   i, j : integer;
  159.   Counter    : array [0..255] of longint;
  160.   CumulCount : integer;
  161. begin
  162.   {clear the counter array}
  163.   FillChar(Counter, sizeof(Counter), 0);
  164.  
  165.   {calculate the distribution of each key}
  166.   for i := 0 to pred(aSize) do begin
  167.     inc(Counter[ord(aInBlock^)]);
  168.     inc(aInBlock);
  169.   end;
  170.  
  171.   {copy over the byte values to the auxiliary list in sorted order,
  172.    generating the start positions for each character as we go}
  173.   CumulCount := 0;
  174.   for i := 0 to 255 do begin
  175.     aStartPos^[i] := CumulCount;
  176.     inc(CumulCount, Counter[i]);
  177.     for j := 0 to pred(Counter[i]) do begin
  178.       aOutBlock^ := char(i);
  179.       inc(aOutBlock);
  180.     end;
  181.   end;
  182. end;
  183. {====================================================================}
  184.  
  185.  
  186. {====================================================================}
  187. function BinarySearch(aList  : PPointerList;
  188.                       aCount : integer;
  189.                       aPtr   : pointer) : integer;
  190. var
  191.   L, R, M : integer;
  192.   CompareResult : integer;
  193. begin
  194.   L := 0;
  195.   R := pred(aCount);
  196.   while (L <= R) do begin
  197.     M := (L + R) div 2;
  198.     CompareResult := CompareBlocks(aPtr, aList^[M], aCount);
  199.     if (CompareResult < 0) then
  200.       R := M - 1
  201.     else if (CompareResult > 0) then
  202.       L := M + 1
  203.     else begin
  204.       Result := M;
  205.       Exit;
  206.     end;
  207.   end;
  208.   Assert(false,
  209.          'BinarySearch: the pointer should be in the list');
  210.   Result := 0;
  211. end;
  212. {====================================================================}
  213.  
  214.  
  215. {====================================================================}
  216. function ApplyBWTransform(aInBlock  : PChar;
  217.                           aOutBlock : PChar;
  218.                           aSize     : integer) : integer;
  219. var
  220.   i : integer;
  221.   DataBlock : PChar;
  222.   PtrList   : PPointerList;
  223.   TempPtr   : PChar;
  224. begin
  225.   {guard against dumb programming mistakes}
  226.   Assert(aInBlock <> nil,
  227.          'ApplyBWTransform: input block cannot be nil');
  228.   Assert(aOutBlock <> nil,
  229.          'ApplyBWTransform: output block cannot be nil');
  230.   Assert(aSize > 0,
  231.          'ApplyBWTransform: block size must be positive');
  232.  
  233.   {prepare for the try..finally}
  234.   DataBlock := nil;
  235.   PtrList := nil;
  236.   try
  237.  
  238.     {allocate the data block and fill it with two copies of the input
  239.      block}
  240.     GetMem(DataBlock, aSize * 2);
  241.     Move(aInBlock^, DataBlock^, aSize);
  242.     Move(aInBlock^, DataBlock[aSize], aSize);
  243.  
  244.     {allocate the list of pointers and set the elements to the
  245.      individual characters in the data block: these will be our
  246.      rotations of the block}
  247.     GetMem(PtrList, aSize * sizeof(pointer));
  248.     TempPtr := DataBlock;
  249.     for i := 0 to pred(aSize) do begin
  250.       PtrList^[i] := TempPtr;
  251.       inc(TempPtr);
  252.     end;
  253.  
  254.     {sort the pointer list}
  255.     Quicksort(PtrList, 0, pred(aSize), aSize);
  256.  
  257.     {calculate the output block}
  258.     for i := 0 to pred(aSize) do
  259.       aOutBlock[i] := PChar(PtrList^[i])[pred(aSize)];
  260.  
  261.     {find the original block in the list}
  262.     Result := BinarySearch(PtrList, aSize, DataBlock);
  263.  
  264.   finally
  265.     FreeMem(DataBlock);
  266.     FreeMem(PtrList);
  267.   end;
  268. end;
  269. {--------}
  270. procedure MoveToFrontDecode(aInBlock  : PChar;
  271.                             aOutBlock : PChar;
  272.                             aSize     : integer);
  273. var
  274.   i, j, k : integer;
  275.   Decoder : array [0..255] of char;
  276.  
  277. begin
  278.   {initialize the encoder array}
  279.   for i := 0 to 255 do
  280.     Decoder[i] := char(i);
  281.  
  282.   {for all the bytes in the input block...}
  283.   for i := 0 to pred(aSize) do begin
  284.  
  285.     {output the character at that position in the decoder array}
  286.     j := ord(aInBlock^);
  287.     aOUtBlock^ := Decoder[j];
  288.  
  289.     {move the character to the front of the decoder array}
  290.     if (j > 0) then
  291.       for k := j downto 1 do
  292.         Decoder[k] := Decoder[k-1];
  293.     Decoder[0] := aOutBlock^;
  294.  
  295.     {advance the input and output pointers}
  296.     inc(aInBlock);
  297.     inc(aOutBlock);
  298.   end;
  299. end;
  300. {--------}
  301. procedure MoveToFrontEncode(aInBlock  : PChar;
  302.                             aOutBlock : PChar;
  303.                             aSize     : integer);
  304. var
  305.   i, j, k : integer;
  306.   Encoder : array [0..255] of char;
  307.  
  308. begin
  309.   {initialize the encoder array}
  310.   for i := 0 to 255 do
  311.     Encoder[i] := char(i);
  312.  
  313.   {for all the characters in the input block...}
  314.   for i := 0 to pred(aSize) do begin
  315.  
  316.     {find it in the encoder array}
  317.     for j := 0 to 255 do
  318.       if (Encoder[j] = aInBlock^) then
  319.         Break;
  320.  
  321.     {output the position}
  322.     aOUtBlock^ := char(j);
  323.  
  324.     {move the character to the front of the encoder array}
  325.     if (j > 0) then
  326.       for k := j downto 1 do
  327.         Encoder[k] := Encoder[k-1];
  328.     Encoder[0] := aInBlock^;
  329.  
  330.     {advance the input and output pointers}
  331.     inc(aInBlock);
  332.     inc(aOutBlock);
  333.   end;
  334. end;
  335. {--------}
  336. procedure UnapplyBWTransform(aInBlock  : PChar;
  337.                              aOutBlock : PChar;
  338.                              aSize     : integer;
  339.                              aIndex    : integer);
  340. var
  341.   i, j : integer;
  342.   FirstCol    : PChar;
  343.   Temp        : PChar;
  344.   TransVector : PIntArray;
  345.   StartPos    : PIntArray;
  346. begin
  347.   {prepare for the try..finally}
  348.   FirstCol := nil;
  349.   TransVector := nil;
  350.   StartPos := nil;
  351.  
  352.   try
  353.  
  354.     {allocate the first column buffer and the transformation vector}
  355.     GetMem(FirstCol, aSize);
  356.     GetMem(TransVector, aSize * sizeof(integer));
  357.     GetMem(StartPos, 256 * sizeof(integer));
  358.  
  359.     {sort the input block using distribution sort}
  360.     DistSort(aInBlock, FirstCol, aSize, StartPos);
  361.  
  362.     {for each character in the unsorted block...}
  363.     Temp := aInBlock;
  364.     for i := 0 to pred(aSize) do begin
  365.  
  366.       {find the next occurrence of this character in the sorted block}
  367.       j := StartPos[ord(Temp^)];
  368.       inc(StartPos[ord(Temp^)]);
  369.  
  370.       {set the entry in the transformation vector}
  371.       TransVector[j] := i;
  372.  
  373.       {advance to the next character in the unsorted block}
  374.       inc(Temp);
  375.     end;
  376.  
  377.     {we now have the transformation vector, so recreate the original
  378.      data starting at the passed in index}
  379.     j := aIndex;
  380.     Temp := aOutBlock;
  381.     for i := 0 to pred(aSize) do begin
  382.       Temp^ := FirstCol[j];
  383.       j := TransVector[j];
  384.       inc(Temp);
  385.     end;
  386.  
  387.   finally
  388.     FreeMem(StartPos);
  389.     FreeMem(TransVector);
  390.     FreeMem(FirstCol);
  391.   end;
  392. end;
  393. {====================================================================}
  394.  
  395.  
  396. {====================================================================}
  397. const
  398.   BWTSignature = $57424141;
  399. {--------}
  400. procedure AABWTCompress(aInStream, aOutStream : TStream);
  401. const
  402.   BufSize = 16*1024;
  403. var
  404.   InBuf     : PChar;
  405.   BWTBuf    : PChar;
  406.   MTFBuf    : PChar;
  407.   BytesRead : integer;
  408.   LongBuf : longint;
  409.   WordBuf : word;
  410.   Index   : integer;
  411. begin
  412.   {prepare for the try..finally}
  413.   InBuf := nil;
  414.   BWTBuf := nil;
  415.   MTFBuf := nil;
  416.  
  417.   try
  418.  
  419.     {allocate the buffers}
  420.     GetMem(InBuf, BufSize);
  421.     GetMem(BWTBuf, BufSize);
  422.     GetMem(MTFBuf, BufSize);
  423.  
  424.     {write the header information to the output stream}
  425.     LongBuf := BWTSignature;
  426.     aOutStream.WriteBuffer(LongBuf, sizeof(LongBuf));
  427.     LongBuf := aInStream.Size;
  428.     aOutStream.WriteBuffer(LongBuf, sizeof(LongBuf));
  429.     WordBuf := BufSize;
  430.     aOutStream.WriteBuffer(WordBuf, sizeof(WordBuf));
  431.  
  432.     {read the first buffer}
  433.     BytesRead := aInStream.Read(InBuf^, BufSize);
  434.  
  435.     {while there is data to compress...}
  436.     while (BytesRead <> 0) do begin
  437.  
  438.       {apply the BWT transform to this buffer}
  439.       Index := ApplyBWTransform(InBuf, BWTBuf, BytesRead);
  440.  
  441.       {write the index to the output stream}
  442.       WordBuf := Index;
  443.       aOutStream.WriteBuffer(WordBuf, sizeof(WordBuf));
  444.  
  445.       {encode the BWT buffer with the Move-To-Front algorithm}
  446.       MoveToFrontEncode(BWTBuf, MTFBuf, BytesRead);
  447.  
  448.       {compress the MTF buffer with Huffman}
  449.       HuffmanCompressBlock(MTFBuf^, BytesRead, aOutStream);
  450.  
  451.       {read the next bufferful}
  452.       BytesRead := aInStream.Read(InBuf^, BufSize);
  453.  
  454.     end;
  455.  
  456.   finally
  457.     FreeMem(MTFBuf);
  458.     FreeMem(BWTBuf);
  459.     FreeMem(InBuf);
  460.   end;
  461. end;
  462. {====================================================================}
  463. procedure AABWTUncompress(aInStream, aOutStream : TStream);
  464. var
  465.   OutBuf    : PChar;
  466.   BWTBuf    : PChar;
  467.   MTFBuf    : PChar;
  468.   BytesRead : integer;
  469.   LongBuf   : longint;
  470.   WordBuf   : word;
  471.   Index     : integer;
  472.   Size      : longint;
  473.   BufSize   : integer;
  474.   BytesToRead : integer;
  475. begin
  476.   {prepare for the try..finally}
  477.   OutBuf := nil;
  478.   BWTBuf := nil;
  479.   MTFBuf := nil;
  480.  
  481.   try
  482.  
  483.     {read the header information from the input stream}
  484.     BytesRead := aInStream.Read(LongBuf, sizeof(LongBuf));
  485.     if (BytesRead <> sizeof(LongBuf)) or
  486.        (LongBuf <> BWTSignature) then
  487.       raise Exception.Create(
  488.          'AABWTUncompress: input stream is not a BWT compressed stream');
  489.     aInStream.ReadBuffer(Size, sizeof(Size));
  490.     aInStream.ReadBuffer(WordBuf, sizeof(WordBuf));
  491.     BufSize := WordBuf;
  492.  
  493.     {allocate the buffers}
  494.     GetMem(OutBuf, BufSize);
  495.     GetMem(BWTBuf, BufSize);
  496.     GetMem(MTFBuf, BufSize);
  497.  
  498.     {while there is still data to uncompress...}
  499.     while (Size <> 0) do begin
  500.  
  501.       {read the index for the next buffer}
  502.       aInStream.ReadBuffer(WordBuf, sizeof(WordBuf));
  503.       Index := WordBuf;
  504.  
  505.       {read and decompress the next block}
  506.       BytesToRead := Size;
  507.       if (BytesToRead > BufSize) then
  508.         BytesToRead := BufSize;
  509.       HuffmanDecompressBlock(aInStream, MTFBuf^, BytesToRead);
  510.  
  511.       {decode using the Move-To-Front algorithm}
  512.       MoveToFrontDecode(MTFBuf, BWTBuf, BytesToRead);
  513.  
  514.       {perform the reverse BWT transform}
  515.       UnapplyBWTransform(BWTBuf, OutBuf, BytesToRead, Index);
  516.  
  517.       {write out the decompressed buffer}
  518.       aOutStream.WriteBuffer(OutBuf^, BytesTORead);
  519.  
  520.       {update the loop variable}
  521.       dec(Size, BytesTORead);
  522.     end;
  523.  
  524.   finally
  525.     FreeMem(MTFBuf);
  526.     FreeMem(BWTBuf);
  527.     FreeMem(OutBuf);
  528.   end;
  529. end;
  530. {====================================================================}
  531.  
  532. end.
  533.